import time
import copy
# encoding: UTF-8

"""
本文件中包含了CTA模块中用到的一些基础设置、类和常量等。
"""

# 把当前根目录添加到python环境变量中
import sys
from itertools import product

import pandas as pd

from .constant import *

sys.path.append('..')


class StopOrder(object):
    """本地停止单"""

    def __init__(self):
        """Constructor"""
        self.vtSymbol = EMPTY_STRING
        self.orderType = EMPTY_UNICODE
        self.direction = EMPTY_UNICODE
        self.offset = EMPTY_UNICODE
        self.price = EMPTY_FLOAT
        self.volume = EMPTY_INT

        self.strategy = None  # 下停止单的策略对象
        self.stopOrderID = EMPTY_STRING  # 停止单的本地编号
        self.status = EMPTY_STRING  # 停止单状态


class KData(object):
    def __init__(self, data=None):
        """
        利用字典初始化KData
        :param data: 包含关键字为['datetime','open','high','low','close',volume']的字典
        """
        if data is None:
            self.datetime = None
            self.open = 0.0
            self.high = 0.0
            self.low = 0.0
            self.close = 0.0
            self.vol = 0
            self.amt = 0.0
        else:
            self.datetime = data['datetime']
            self.open = data['open']
            self.high = data['high']
            self.low = data['low']
            self.close = data['close']
            self.vol = data['vol']
            self.amt = data['amt']


class KLine(pd.DataFrame):
    """K线
    index：datetime,
    open,high,low,close,volume
    period
    """

    def __init__(self, period=1, data=None):
        super(KLine, self).__init__()
        self.period = period
        if data is not None:
            self.index = data.index
            self.open = data['open']
            self.high = data['high']
            self.low = data['low']
            self.close = data['close']
            self.volume = data['volume']


class CtaBarData(object):
    """K线数据"""

    def __init__(self):
        """Constructor"""
        self.vtSymbol = EMPTY_STRING  # vt系统代码
        self.symbol = EMPTY_STRING  # 代码
        self.exchange = EMPTY_STRING  # 交易所

        self.open = EMPTY_FLOAT  # OHLC
        self.high = EMPTY_FLOAT
        self.low = EMPTY_FLOAT
        self.close = EMPTY_FLOAT

        self.date = EMPTY_STRING  # bar开始的时间，日期
        self.time = EMPTY_STRING  # 时间
        self.datetime = None  # python的datetime时间对象

        self.volume = EMPTY_INT  # 成交量
        self.openInterest = EMPTY_INT  # 持仓量
        self.period = 1  # K线周期


class BarData(object):
    """K线数据"""

    def __init__(self):
        """Constructor"""
        self.symbol = EMPTY_STRING  # 代码
        self.exchange = EMPTY_STRING  # 交易所

        self.open = EMPTY_FLOAT  # OHLC
        self.high = EMPTY_FLOAT
        self.low = EMPTY_FLOAT
        self.close = EMPTY_FLOAT

        self.date = EMPTY_STRING  # bar开始的时间，日期
        self.time = EMPTY_STRING  # 时间
        self.datetime = None  # python的datetime时间对象

        self.volume = EMPTY_INT  # 成交量
        self.openInterest = EMPTY_INT  # 持仓量
        self.period = 1  # K线周期


class TickData(object):
    """Tick数据"""

    def __init__(self):
        """Constructor"""
        self.instrument_id = EMPTY_STRING  # 代码
        self.symbol = EMPTY_STRING  # 合约代码
        self.exchange = EMPTY_STRING  # 交易所代码

        # 成交数据
        self.lastPrice = EMPTY_FLOAT  # 最新成交价
        self.volume = EMPTY_INT  # 最新成交量
        self.openInterest = EMPTY_INT  # 持仓量
        self.amount = EMPTY_FLOAT

        self.upperLimit = EMPTY_FLOAT  # 涨停价
        self.lowerLimit = EMPTY_FLOAT  # 跌停价

        # tick的时间
        self.date = EMPTY_STRING  # 日期
        self.time = EMPTY_STRING  # 时间
        self.datetime = None  # python的datetime时间对象

        # 五档行情
        self.bidPrice1 = EMPTY_FLOAT
        self.bidPrice2 = EMPTY_FLOAT
        self.bidPrice3 = EMPTY_FLOAT
        self.bidPrice4 = EMPTY_FLOAT
        self.bidPrice5 = EMPTY_FLOAT

        self.askPrice1 = EMPTY_FLOAT
        self.askPrice2 = EMPTY_FLOAT
        self.askPrice3 = EMPTY_FLOAT
        self.askPrice4 = EMPTY_FLOAT
        self.askPrice5 = EMPTY_FLOAT

        self.bidVolume1 = EMPTY_INT
        self.bidVolume2 = EMPTY_INT
        self.bidVolume3 = EMPTY_INT
        self.bidVolume4 = EMPTY_INT
        self.bidVolume5 = EMPTY_INT

        self.askVolume1 = EMPTY_INT
        self.askVolume2 = EMPTY_INT
        self.askVolume3 = EMPTY_INT
        self.askVolume4 = EMPTY_INT
        self.askVolume5 = EMPTY_INT


class CtaTickData(object):
    """Tick数据"""

    def __init__(self):
        """Constructor"""
        self.vtSymbol = EMPTY_STRING  # vt系统代码
        self.symbol = EMPTY_STRING  # 合约代码
        self.exchange = EMPTY_STRING  # 交易所代码

        # 成交数据
        self.lastPrice = EMPTY_FLOAT  # 最新成交价
        self.volume = EMPTY_INT  # 最新成交量
        self.openInterest = EMPTY_INT  # 持仓量
        self.amount = EMPTY_FLOAT

        self.upperLimit = EMPTY_FLOAT  # 涨停价
        self.lowerLimit = EMPTY_FLOAT  # 跌停价

        # tick的时间
        self.date = EMPTY_STRING  # 日期
        self.time = EMPTY_STRING  # 时间
        self.datetime = None  # python的datetime时间对象

        # 五档行情
        self.bidPrice1 = EMPTY_FLOAT
        self.bidPrice2 = EMPTY_FLOAT
        self.bidPrice3 = EMPTY_FLOAT
        self.bidPrice4 = EMPTY_FLOAT
        self.bidPrice5 = EMPTY_FLOAT

        self.askPrice1 = EMPTY_FLOAT
        self.askPrice2 = EMPTY_FLOAT
        self.askPrice3 = EMPTY_FLOAT
        self.askPrice4 = EMPTY_FLOAT
        self.askPrice5 = EMPTY_FLOAT

        self.bidVolume1 = EMPTY_INT
        self.bidVolume2 = EMPTY_INT
        self.bidVolume3 = EMPTY_INT
        self.bidVolume4 = EMPTY_INT
        self.bidVolume5 = EMPTY_INT

        self.askVolume1 = EMPTY_INT
        self.askVolume2 = EMPTY_INT
        self.askVolume3 = EMPTY_INT
        self.askVolume4 = EMPTY_INT
        self.askVolume5 = EMPTY_INT


class StrategyParamData(object):
    def __init__(self):
        self.name = EMPTY_STRING  # 参数名称
        self.min = EMPTY_INT  # 参数最小值
        self.max = EMPTY_INT  # 参数最大值
        self.step = EMPTY_INT  # 参数优化时步长
        self.multiplier = 1  # 参数乘数，当该字段不为1时，在具体应用中参数值要除以该字段，它的存在是为了保证参数为整数
        self.description = EMPTY_STRING  # 参数说明
        self.default = EMPTY_INT  # 参数缺省值，可能是最优值


class StraParam(object):
    """策略参数"""

    def __init__(self, param=None):
        self._id = EMPTY_STRING  # 参数代码
        self.name = EMPTY_STRING  # 参数名称
        self.min = EMPTY_INT  # 参数最小值
        self.max = EMPTY_INT  # 参数最大值
        self.step = EMPTY_INT  # 参数优化时步长
        self.multiplier = 1  # 参数乘数，当该字段不为1时，在具体应用中参数值要除以该字段，它的存在是为了保证参数为整数
        self.description = EMPTY_STRING  # 参数说明
        self.default = EMPTY_INT  # 参数缺省值，可能是最优值

        self.symbol = EMPTY_STRING  # 当用于实盘时，适用的标的
        self.period = EMPTY_INT  # 当用于实盘时，最优周期，与其对应的值是default

        if param:
            d = self.__dict__
            for k, v in param:
                if k in d.keys():
                    d[k] = v


class StrategyVariable(object):
    """策略变量"""

    def __init__(self):
        self.name = EMPTY_STRING  # 变量名称
        self.value = EMPTY_INT
        self.out_type = VARIABLE_OUT_TYPE_NONE


class StraSetting(object):
    def __init__(self, params=None, symbol='', pkg='', name='', class_name='', period=0, strategy_type='',
                 description='', data_len=0, days=10, used=0, sd='', ed=''):
        self.params = params
        self.symbol = symbol
        self.pkg = pkg  # 策略类所在的包名称，是一完整路径，如：boxquant.strategy.trend_strategy
        self.name = name  # 策略实例名称
        self.class_name = class_name  # 策略类名称
        self.period = period
        self.strategy_type = strategy_type
        self.description = description
        self.data_len = data_len
        self.days = days
        self.sd = sd
        self.ed = ed
        self.used = used
        self.other_data = None  # {symbol:symbol,period:period,sd:start_datetime,ed:end_datetime,dlen:data_len}
        self.instances = None


class StrategySignal(object):
    """策略信号"""

    def __init__(self):
        self.id = EMPTY_STRING  # strategy instance id
        self.params: dict = {}
        self.variables: dict = {}
        self.symbol = EMPTY_STRING
        self.oc_flag = STRATEGY_SIGNAL_OFFSET_NONE
        self.ls_flag = STRATEGY_SIGNAL_DIRECTION_NONE
        self.bssc = 0  # 0: money, 1: buy, -1: sell, -2: short, 2: cover, 4: cover and buy, -4: sell and short
        self.datetime = None  # 策略信号时间 2010-01-10 hh:mm:ss
        self.price = EMPTY_FLOAT
        self.vol = EMPTY_FLOAT  # >1: 为手数，<1 and >0:可用手数的百分比，<0:无效
        self.stop_price = EMPTY_FLOAT
        self.order_type = STRATEGY_ORDER_NONE


class TradingResult(object):
    """每笔交易的结果"""

    def __init__(self, entryPrice, entryDt, exitPrice,
                 exitDt, volume, rate, slippage, size, tradeDirection=DIRECTION_LONG):
        """Constructor"""
        self.tradeDirection = tradeDirection
        self.entryPrice = entryPrice  # 开仓价格
        self.exitPrice = exitPrice  # 平仓价格

        self.entryDt = entryDt  # 开仓时间datetime
        self.exitDt = exitDt  # 平仓时间

        self.vol = volume  # 交易数量（+/-代表方向）

        self.turnover = (self.entryPrice + self.exitPrice) * size * abs(volume)  # 成交金额
        if rate < 1:
            self.commission = self.turnover * rate  # 手续费成本
        else:
            self.commission = abs(volume) * rate
        self.slippage = slippage * 2 * size * abs(volume)  # 滑点成本
        self.pnl = ((self.exitPrice - self.entryPrice) * volume * size
                    - self.commission - self.slippage)  # 净盈亏


class OptimizationSetting(object):
    """优化设置"""

    def __init__(self):
        """Constructor"""
        self.param_dict = {}
        self.param_setting = []

        self.optimize_target = ''  # 优化目标字段

    def add_parameter(self, name, start, end=None, step=None):
        """增加优化参数"""
        if end is None and step is None:
            self.param_dict[name] = [start]
            return

        if end <= start:
            print('参数起始点必须小于终止点')
            return

        if step <= 0:
            print('参数步进必须大于0')
            return

        l = []
        param = start

        while param <= end:
            l.append(param)
            param += step

        self.param_dict[name] = l

    def generate_setting(self):
        """生成优化参数组合"""
        # 参数名的列表
        nameList = list(self.param_dict.keys())
        paramList = list(self.param_dict.values())

        # 使用迭代工具生产参数对组合
        productList = list(product(*paramList))

        # 把参数对组合打包到一个个字典组成的列表中
        settingList = []
        for p in productList:
            d = dict(list(zip(nameList, p)))
            settingList.append(d)
        self.param_setting = settingList
        return settingList

    def setOptimizeTarget(self, target):
        """设置优化目标字段"""
        self.optimize_target = target
        self.lperiod = EMPTY_INT


def format_number(n, max_decimals=2, delimeter=''):
    """格式化数字到字符串"""
    n = round(n, max_decimals)  # 保留两位小数
    return format(n, delimeter)  # 加上千分符




class Bar(object):
    """K线数据"""

    def __init__(self, datetime: str, ins: str, h: float, l: float, o: float, c: float, v: int, i: float,
                 tradingday: str):
        """初始化"""
        """时间
        yyyyMMdd HH:mm:ss"""
        self.D = datetime
        """时间
        yyyyMMdd HH:mm:ss"""
        """合约"""
        self.Instrument = ins
        """合约"""
        """交易日"""
        self.Tradingday = tradingday
        """交易日"""
        """最高价"""
        self.H = h
        """最高价"""
        """最低价"""
        self.L = l
        """最低价"""
        """开仓价"""
        self.O = o
        """开仓价"""
        """收盘价"""
        self.C = c
        """收盘价"""
        """成交量"""
        self.V = v
        """成交量"""
        """持仓价"""
        self.I = i
        """持仓价"""
        self._pre_volume = 0

    def __str__(self):
        """"""
        return '{self.D}, {self.H}, {self.L}, {self.O}, {self.C}, {self.V}, {self.I}'.format(
            self=self)


class Data(object):
    '''数据类, 策略继承此类'''

    def __init__(self, stra_barupdate=None, stra_onorder=None):
        '''初始所有变量'''
        self.stra_uppdate = stra_barupdate
        self.stra_onorder = stra_onorder
        '''每bar只执行一次交易'''
        self.SingleOrderOneBar = False
        '''每bar只执行一 次交易'''
        '''K线序列'''
        self.Bars = []
        '''K线序列'''
        '''合约'''
        self.Instrument = ''
        '''合约'''
        '''周期'''
        self.Interval = 1
        '''周期'''
        '''周期类型'''
        self.IntervalType = IntervalType.Minute
        '''周期类型'''

        '''买卖信号'''
        self.Orders = []
        '''买卖信号'''

        '''指标字典
        策略使用的指标保存在此字典中
        以便管理程序显示和处理'''
        self.IndexDict = {}
        '''指标字典
        策略使用的指标保存在此字典中
        以便管理程序显示和处理'''
        '''时间'''
        self.D = []
        '''时间'''
        '''最高价'''
        self.H = []
        '''最高价'''
        '''最低价'''
        self.L = []
        '''最低价'''
        '''开盘价'''
        self.O = []
        '''开盘价'''
        '''收盘价'''
        self.C = []
        '''收盘价'''
        '''交易量'''
        self.V = []
        '''交易量'''
        '''持仓量'''
        self.I = []
        '''持仓量'''

        self._lastOrder = OrderItem()

    @property
    def AvgEntryPriceShort(self):
        '''开仓均价-空'''
        return self._lastOrder.AvgEntryPriceShort

    @property
    def AvgEntryPriceLong(self):
        '''开仓均价-多'''
        return self._lastOrder.AvgEntryPriceLong

    @property
    def PositionLong(self):
        '''持仓-多'''
        return self._lastOrder.PositionLong

    @property
    def PositionShort(self):
        '''持仓-空'''
        return self._lastOrder.PositionShort

    @property
    def EntryDateLong(self):
        '''开仓时间-多'''
        return self._lastOrder.EntryDateLong

    @property
    def EntryPriceLong(self):
        '''开仓价格-多'''
        return self._lastOrder.EntryPriceLong

    @property
    def ExitDateShort(self):
        '''平仓时间-空'''
        return self._lastOrder.ExitDateShort

    @property
    def ExitPriceShort(self):
        '''平仓价-空'''
        return self._lastOrder.ExitPriceShort

    @property
    def EntryDateShort(self):
        '''开仓时间-空'''
        return self._lastOrder.EntryDateShort

    @property
    def EntryPriceShort(self):
        '''开仓价-空'''
        return self._lastOrder.EntryPriceShort

    @property
    def ExitDateLong(self):
        '''平仓时间-多'''
        return self._lastOrder.ExitDateLong

    @property
    def ExitPriceLong(self):
        '''平仓价-多'''
        return self._lastOrder.ExitPriceLong

    @property
    def LastEntryDateShort(self):
        '''最后开仓时间-空'''
        return self._lastOrder.LastEntryDateShort

    @property
    def LastEntryPriceShort(self):
        '''最后开仓价-空'''
        return self._lastOrder.LastEntryPriceShort

    @property
    def LastEntryDateLong(self):
        '''最后开仓时间-多'''
        return self._lastOrder.LastEntryDateLong

    @property
    def LastEntryPriceLong(self):
        '''最后开仓价-多'''
        return self._lastOrder.LastEntryPriceLong

    @property
    def IndexEntryLong(self):
        '''开仓到当前K线数量-多'''
        return self._lastOrder.IndexEntryLong

    @property
    def IndexEntryShort(self):
        '''开仓到当前K线数量-空'''
        return self._lastOrder.IndexEntryShort

    @property
    def IndexLastEntryLong(self):
        '''最后开仓到当前K线的数量-多'''
        return self._lastOrder.IndexLastEntryLong

    @property
    def IndexLastEntryShort(self):
        '''最后开仓到当前K线的数量-空'''
        return self._lastOrder.IndexLastEntryShort

    @property
    def IndexExitLong(self):
        '''平仓到当前K线数量-多'''
        return self._lastOrder.IndexExitLong

    @property
    def IndexExitShort(self):
        '''平仓到当前K线数量-空'''
        return self._lastOrder.IndexExitShort

    @property
    def Position(self):
        '''持仓净头寸'''
        return self.PositionLong - self.PositionShort

    @property
    def CurrentBar(self):
        '''当前K线序号(0开始)'''
        return max(len(self.Bars) - 1, 0)

    def on_min(self, bar: Bar):
        '''分笔数据处理'''
        if len(self.Bars) == 0 or self.Bars[-1].D != bar.D:  # 新数据
            self.__new_min_bar__(bar)  # 新K线数据插入
        else:
            self.__update_bar__(bar)

    def __new_min_bar__(self, bar2):
        """有新min_bar添加"""
        bar = copy.copy(bar2)
        bar_time = time.strptime(bar.D, "%Y-%m-%d %H:%M:%S")
        year = bar_time.tm_year
        mon = bar_time.tm_mon
        day = bar_time.tm_mday
        hour = bar_time.tm_hour
        mins = bar_time.tm_min
        if self.IntervalType == IntervalType.Minute:
            mins = bar_time.tm_min // self.Interval * self.Interval
        elif self.IntervalType == IntervalType.Hour:
            hour = hour // self.Interval
            mins = 0
        elif self.IntervalType == IntervalType.Day:
            hour = 0
            mins = 0
        elif self.IntervalType == IntervalType.Month:
            hour = 0
            mins = 0
            day = 1
        elif self.IntervalType == IntervalType.Year:
            hour = 0
            mins = 0
            day = 1
            mon = 1
        elif self.IntervalType == IntervalType.Week:
            hour = 0
            mins = 0
            # 用周号替换日期
            day = time.strftime('%W', bar_time)

        # time -> str
        bar_time = f'{year}-{mon:02d}-{day:02d} {hour:02d}:{mins:02d}:00'
        if len(self.Bars) == 0 or self.Bars[-1].D != bar_time:
            bar.D = bar_time
            self.Bars.append(bar)

            self.D.append(bar.D)
            self.H.append(bar.H)
            self.L.append(bar.L)
            self.O.append(bar.O)
            self.C.append(bar.C)
            self.V.append(bar.V)
            self.I.append(bar.I)
        else:
            old_bar = self.Bars[-1]
            self.H[-1] = old_bar.H = max(bar.H, old_bar.H)
            self.L[-1] = old_bar.L = min(bar.L, old_bar.L)
            self.C[-1] = old_bar.C = bar.C
            old_bar.V += bar.V
            self.V[-1] = old_bar.V
            self.I[-1] = old_bar.I = bar.I
            # bar.A = tick.AveragePrice

        self.stra_uppdate(self, bar)

    def __update_bar__(self, bar):
        """更新当前数据序列"""

        self.D[-1] = bar.D
        self.H[-1] = bar.H
        self.L[-1] = bar.L
        self.O[-1] = bar.O
        self.C[-1] = bar.C
        self.V[-1] = bar.V
        self.I[-1] = bar.I
        self.stra_uppdate(self, bar)

    def __order__(self, direction, offset, price, volume, remark):
        """策略执行信号"""
        # if self.SingleOrderOneBar:
        #     # 平仓后允许开仓
        #     if self.ExitDateShort == self.D[-1] and (not (direction == DirectType.Buy and offset == OffsetType.Open)):
        #         return
        #     if self.ExitDateLong == self.D[-1] and (not (direction == DirectType.Sell and offset == OffsetType.Open)):
        #         return
        #     if self.LastEntryDateLong == self.D[-1] or self.LastEntryDateShort == self.D[-1]:
        #         return
        if self.SingleOrderOneBar and (self.LastEntryDateLong == self.D[-1]
                                       or self.LastEntryDateShort == self.D[-1]
                                       or self.ExitDateLong == self.D[-1]
                                       or self.ExitDateShort == self.D[-1]):
            return
        order = OrderItem()
        order.Instrument = self.Instrument
        order.DateTime = self.D[-1]
        order.Direction = direction
        order.Offset = offset
        order.Price = price
        order.Volume = volume
        order.Remark = remark

        self.Orders.append(order)

        # 处理策略相关属性
        order.IndexEntryLong = self._lastOrder.IndexEntryLong
        order.IndexExitLong = self._lastOrder.IndexExitLong
        order.IndexEntryShort = self._lastOrder.IndexEntryShort
        order.IndexExitShort = self._lastOrder.IndexExitShort
        order.IndexLastEntryLong = self._lastOrder.IndexLastEntryLong
        order.IndexLastEntryShort = self._lastOrder.IndexLastEntryShort
        order.AvgEntryPriceLong = self._lastOrder.AvgEntryPriceLong
        order.AvgEntryPriceShort = self._lastOrder.AvgEntryPriceShort
        order.PositionLong = self._lastOrder.PositionLong
        order.PositionShort = self._lastOrder.PositionShort
        order.EntryDateLong = self._lastOrder.EntryDateLong
        order.EntryDateShort = self._lastOrder.EntryDateShort
        order.EntryPriceLong = self._lastOrder.EntryPriceLong
        order.EntryPriceShort = self._lastOrder.EntryPriceShort
        order.ExitDateLong = self._lastOrder.ExitDateLong
        order.ExitDateShort = self._lastOrder.ExitDateShort
        order.ExitPriceLong = self._lastOrder.ExitPriceLong
        order.ExitPriceShort = self._lastOrder.ExitPriceShort
        order.LastEntryDateLong = self._lastOrder.LastEntryDateLong
        order.LastEntryDateShort = self._lastOrder.LastEntryDateShort
        order.LastEntryPriceLong = self._lastOrder.LastEntryPriceLong
        order.LastEntryPriceShort = self._lastOrder.LastEntryPriceShort

        diroff = '{0}-{1}'.format(order.Direction.name, order.Offset.name)
        if diroff == 'Buy-Open':
            if self._lastOrder.PositionLong == 0:
                order.IndexEntryLong = len(self.Bars) - 1
                order.EntryDateLong = self.D[-1]  # str '20160630 21:25:00'
                order.EntryPriceLong = order.Price
                order.PositionLong = order.Volume
                order.AvgEntryPriceLong = order.Price
            else:
                order.PositionLong += order.Volume
                order.AvgEntryPriceLong = (
                                                  self._lastOrder.PositionLong * self._lastOrder.AvgEntryPriceLong + order.Volume * order.Price) / (
                                                  self._lastOrder.PositionLong + order.Volume)
            order.IndexLastEntryLong = len(self.Bars) - 1
            order.LastEntryPriceLong = order.Price
            order.LastEntryDateLong = self.D[-1]
        elif diroff == 'Buy-Close':
            c_lots = min(self._lastOrder.PositionShort, order.Volume)  # 能够平掉的数量
            if c_lots > 0:  # 避免无仓可平
                order.PositionShort -= c_lots

                order.IndexExitShort = len(self.Bars) - 1
                order.ExitDateShort = self.D[-1]
                order.ExitPriceShort = order.Price
                # if order.PositionShort == 0:
                # order.AvgEntryPriceShort = 0  # 20180906注销方便后期计算盈利
        elif diroff == 'Sell-Open':
            if self._lastOrder.PositionShort == 0:
                order.IndexEntryShort = len(self.Bars) - 1
                order.EntryDateShort = self.D[-1]  # time or double or str ???
                order.EntryPriceShort = order.Price
                order.AvgEntryPriceShort = order.Price
                order.PositionShort = order.Volume
            else:
                order.PositionShort += order.Volume
                order.AvgEntryPriceShort = (
                                                   self._lastOrder.PositionShort * self._lastOrder.AvgEntryPriceShort + order.Volume * order.Price) / (
                                                   self._lastOrder.PositionShort + order.Volume)
            order.IndexLastEntryShort = len(self.Bars) - 1
            order.LastEntryPriceShort = order.Price
            order.LastEntryDateShort = self.D[-1]
        elif diroff == 'Sell-Close':
            c_lots = min(self._lastOrder.PositionLong, order.Volume)  # 能够平掉的数量
            if c_lots > 0:  # 避免无仓可平
                order.PositionLong -= c_lots

                order.IndexExitLong = len(self.Bars) - 1
                order.ExitDateLong = self.D[-1]
                order.ExitPriceLong = order.Price
                # if order.PositionLong == 0:
                # order.AvgEntryPriceLong = 0  # 20180906注销方便后期计算盈利

        self._lastOrder = order
        self.stra_onorder(self, order)

    def Buy(self, price: float, volume: int, remark: str = ''):
        """买开"""
        self.__order__(DirectType.Buy, OffsetType.Open, price, volume, remark)

    def Sell(self, price, volume, remark: str = ''):
        """买平"""
        self.__order__(DirectType.Sell, OffsetType.Close, price, volume,
                       remark)

    def SellShort(self, price, volume, remark: str = ''):
        """卖开"""
        self.__order__(DirectType.Sell, OffsetType.Open, price, volume, remark)

    def BuyToCover(self, price, volume, remark: str = ''):
        """买平"""
        self.__order__(DirectType.Buy, OffsetType.Close, price, volume, remark)
